package com.myhystrix;

import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandMetrics;
import com.netflix.hystrix.HystrixCommandProperties;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 参考地址https://blog.csdn.net/pfnie/article/details/82317184
 * https://www.jianshu.com/p/e3408619718d
 */
public interface HystrixCircuitBreaker {
    /**
     * 每个@link hystrixcommand请求都询问是否允许继续
     * 这考虑了半开逻辑，当确定是否应该再次关闭它时，它允许一些请求通过
     */
    public boolean allowRequest();

    /**
     * Whether the circuit is currently open (tripped).
     * 电路当前是否开路（跳闸）
     * @return boolean state of circuit breaker
     */
    public boolean isOpen();

    /**
     以@link hystrixcommand成功执行为反馈机制的一部分。
     */
    void markSuccess();



    class Factory {
        //circuitBreakersByCommand 是个ConcurrentHashMap， 这里缓存了系统的所有熔断器
        // String is HystrixCommandKey.name() (we can't use HystrixCommandKey directly as we can't guarantee it implements hashcode/equals correctly)
        private static ConcurrentHashMap<String, HystrixCircuitBreaker> circuitBreakersByCommand = new ConcurrentHashMap<String, HystrixCircuitBreaker>();

        public static HystrixCircuitBreaker getInstance(HystrixCommandKey key, HystrixCommandGroupKey group,
                                                        HystrixCommandProperties properties, HystrixCommandMetrics metrics) {
            //先从缓存里取 先前缓存的
            HystrixCircuitBreaker previouslyCached = circuitBreakersByCommand.get(key.name());
            if (previouslyCached != null) {
                return previouslyCached;
            }
            //取不到对象才会创建个HystrixCircuitBreakerImpl对象并放入缓存Map中
            HystrixCircuitBreaker cbForCommand = circuitBreakersByCommand.putIfAbsent(key.name(), new HystrixCircuitBreakerImpl(key, group, properties, metrics));
            if (cbForCommand == null) { //说明之前不存在
                // this means the putIfAbsent step just created a new one so let's retrieve and return it
                //putifess步骤刚刚创建了一个新步骤，所以让我们检索并返回它。
                return circuitBreakersByCommand.get(key.name());
            } else {
                // this means a race occurred and while attempting to 'put' another one got there before
                // and we instead retrieved it and will now return it
                return cbForCommand;
            }
        }

        public static HystrixCircuitBreaker getInstance(HystrixCommandKey key) {
            return circuitBreakersByCommand.get(key.name());
        }

        /**
         * Clears all circuit breakers. If new requests come in instances will be recreated.
         * 清除所有断路器。如果新请求传入，将重新创建实例
         */
        /* package */static void reset() {
            circuitBreakersByCommand.clear();
        }

    }

    //断路由有三种状态 ，分别为关闭，打开，半开状态

    /**
     * HystrixCircuitBreakerImpl  这个类里定义了一个状态变量，断路由有三种状态 ，分别为关闭，打开，半开状态。
     * 重点关注下allowRequest方法，在allowRequest里首先判断forceOpen属性是否打开，如果打开则不允许有请求进入，
     * 然后forceClosed属性，如果这个属性为true,刚对所有的求求放行，相当于熔断器不起作用。之后就是状态判断了。
     * isAfterSleepWindow（）方法用于放行超过了指定时间后的流量，。具体代码如下，关键部分有相应的注释：
     *
     */
     static class HystrixCircuitBreakerImpl implements HystrixCircuitBreaker {
        private HystrixCommandProperties properties;
        private HystrixCommandMetrics metrics;

        //三种状态通过枚举来定义
        enum Status {
            CLOSED, OPEN, HALF_OPEN;
        }

        //状态变时，默认是关闭的状态
        private AtomicBoolean circuitOpen = new AtomicBoolean(false);

        //最后一次访问的时间，用于试探请求是否恢复
        private AtomicLong circuitOpenedOrLastTestedTime = new AtomicLong();

        public HystrixCircuitBreakerImpl(HystrixCommandKey key, HystrixCommandGroupKey group, HystrixCommandProperties properties, HystrixCommandMetrics metrics) {
        }

        @Override
        public boolean allowRequest() {
            //断路器强制打开的情况下
            if (((Boolean)this.properties.circuitBreakerForceOpen().get()).booleanValue()) {
                return false;
            } else if (((Boolean)this.properties.circuitBreakerForceClosed().get()).booleanValue()) {
                this.isOpen();
                return true;
            } else {
                return !this.isOpen();
            }
        }

        ////用于判断熔断器是否打开
        @Override
        public boolean isOpen() {
            //熔断器打开
            if (this.circuitOpen.get()) {
                return true;
            } else {
                //根据法制
                HystrixCommandMetrics.HealthCounts health = this.metrics.getHealthCounts();
                if (health.getTotalRequests() < (long)((Integer)this.properties.circuitBreakerRequestVolumeThreshold().get()).intValue()) {
                    return false;
                } else if (health.getErrorPercentage() < ((Integer)this.properties.circuitBreakerErrorThresholdPercentage().get()).intValue()) {
                    return false;
                } else if (this.circuitOpen.compareAndSet(false, true)) { //超过了上面的法制了
                    this.circuitOpenedOrLastTestedTime.set(System.currentTimeMillis());
                    return true;
                } else {
                    return true;
                }
            }
        }

        @Override
        public void markSuccess() {

        }

        public void markSuccess2() {
            System.out.println("444");
        }

        public void markSuccess355() {
            System.out.println("3555");
        }


        public void markSuccess80900() {
            System.out.println("890000");
        }
    }

}
